package org.tech4j.common.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import org.apache.commons.lang3.StringUtils;
import org.tech4j.common.utils.exception.JsonException;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @program: tech4j-common
 * @description: Json 转换工具类
 * @author: YuKai Fan
 * @create: 2025/3/1 11:11
 **/
public class JsonUtils {

    private final static ObjectMapper objectMapper = new ObjectMapper();

    private JsonUtils() {
    }

    static {
        // 对象属性为null，序列化不显示
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 序列化命名策略——驼峰命名
        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
        // 反序列化时，忽略未知字段
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 反序列化时，可以解析带注释的JSON
        objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
        // 允许处理JSON中的非数值(NaN, 正无穷大，负无穷大)
        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        // 允许yaml 注释
        objectMapper.configure(JsonParser.Feature.ALLOW_YAML_COMMENTS, true);
    }

    public static ObjectMapper getObjectMapper() {
        return objectMapper;
    }

    public static void configure(JsonParser.Feature feature, boolean state) {
        objectMapper.configure(feature, state);
    }

    public static void configure(DeserializationFeature feature, boolean state) {
        objectMapper.configure(feature, state);
    }

    public static void configure(JsonParser.Feature[] features, boolean state) {
        for (JsonParser.Feature feature : features) {
            objectMapper.configure(feature, state);
        }
    }

    public static void configure(DeserializationFeature[] features, boolean state) {
        for (DeserializationFeature feature : features) {
            objectMapper.configure(feature, state);
        }
    }

    /**
     * 转为json string
     *
     * @param t
     * @return
     */
    public static <T> String toJsonString(T t) {
        if (Objects.isNull(t)) {
            return null;
        }
        try {
            return objectMapper.writeValueAsString(t);
        } catch (JsonProcessingException e) {
            throw new JsonException("JsonUtils toJsonString error", e);
        }
    }

    /**
     * bean 转为bytes
     * @param t
     * @return
     * @param <T>
     */
    public static <T> byte[] toBytes(T t) {
        try {
            return objectMapper.writeValueAsBytes(t);
        } catch (JsonProcessingException e) {
            throw new JsonException("JsonUtils toBytes error", e);
        }
    }

    /**
     * 转为class实例
     *
     * @param json
     * @param clazz
     * @return
     */
    public static <T> T parse(String json, Class<T> clazz) {
        if (StringUtils.isBlank(json)) {
            return null;
        }
        try {
            return objectMapper.readValue(json, clazz);
        } catch (Exception e) {
            throw new JsonException("JsonUtils parse from string error", e);
        }
    }

    /**
     * bytes convert bean
     *
     * @param bytes
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T parse(byte[] bytes, Class<T> clazz) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        try {
            return objectMapper.readValue(bytes, clazz);
        } catch (Exception e) {
            throw new JsonException("JsonUtils parse from bytes error", e);
        }
    }


    /**
     * map转为bean
     * @param map
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T parse(Map<String, Object> map, Class<T> clazz) {
        if (map == null || map.isEmpty()) {
            return null;
        }
        return objectMapper.convertValue(map, clazz);
    }

    /**
     * 转为class实例
     *
     * @param json
     * @param reference
     * @return
     */
    public static <T> T parse(String json, TypeReference<T> reference) {
        if (StringUtils.isBlank(json)) {
            return null;
        }
        try {
            return objectMapper.readValue(json, reference);
        } catch (Exception e) {
            throw new JsonException("JsonUtils parse from string error", e);
        }
    }


    /**
     * byte转为JsonNode
     *
     * @param bytes
     * @return
     */
    public static JsonNode parseJsonNode(byte[] bytes) {
        try {
            return objectMapper.readTree(bytes);
        } catch (Exception e) {
            throw new JsonException("JsonUtils parseJsonNode form bytes error", e);
        }
    }

    /**
     * String转为JsonNode
     *
     * @param json
     * @return
     */
    public static JsonNode parseJsonNode(String json) {
        try {
            return objectMapper.readTree(json);
        } catch (Exception e) {
            throw new JsonException("JsonUtils parseJsonNode form string error", e);
        }
    }

    /**
     * json string转为map
     *
     * @param json
     * @return
     */
    public static Map<String, Object> toObjectMap(String json) {
        if (StringUtils.isEmpty(json)) {
            return null;
        }
        try {
            TypeReference<LinkedHashMap<String, Object>> typeRef = new TypeReference<>() {};
            return objectMapper.readValue(json, typeRef);
        } catch (Exception e) {
            throw new JsonException("JsonUtils toObjectMap error", e);
        }
    }

    /**
     * json string转为map
     *
     * @param json
     * @param <T>
     * @return
     */
    public static <K, V>  Map<K, V> toObjectMap(String json, Class<K> kClazz, Class<V> vClazz) {
        if (StringUtils.isEmpty(json)) {
            return null;
        }
        try {
            JavaType javaType = objectMapper.getTypeFactory().constructParametricType(Map.class, kClazz, vClazz);
            return objectMapper.readValue(json, javaType);
        } catch (Exception e) {
            throw new JsonException(String.format("JsonUtils toObjectMap<%s, %s> error", kClazz.getName(), vClazz.getName()), e);
        }
    }

    /**
     * json string转为map
     *
     * Key为map
     *
     * @param json
     * @param <V>
     * @return
     */
    public static <V>  Map<String, V> toObjectMap(String json, Class<V> vClazz) {
        return toObjectMap(json, String.class, vClazz);
    }


    /**
     * json string转为map
     *
     * Key为map
     *
     * @param json
     * @param <V>
     * @return
     */
    public static Map<String, String> toStringMap(String json) {
        return toObjectMap(json, String.class, String.class);
    }


    /**
     * json字符串转成list
     *
     * @param jsonString
     * @param cls
     * @return
     */
    public static <T> List<T> parseArray(String jsonString, Class<T> cls) {
        if (StringUtils.isBlank(jsonString)) {
            return Collections.emptyList();
        }
        try {
            return objectMapper.readValue(jsonString, getCollectionType(List.class, cls));
        } catch (Exception e) {
            throw new JsonException(String.format("JsonUtils toJsonList<%s> error", cls.getName()), e);
        }
    }

    /**
     * 获取泛型的Collection Type
     *
     * @param collectionClass 泛型的Collection
     * @param elementClasses  实体bean
     * @return JavaType Java类型
     */
    public static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
        if (elementClasses == null || elementClasses.length == 0) {
            throw new IllegalArgumentException("elementClasses must not be empty");
        }
        return objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
    }
}
